//! # Event - 事件模块
//!
//! 记录了应用的所有事件类型，以及对应的 Payload 类型。
//!
//! 并且提供了相应的序列化和反序列化方法。

use serde::{Deserialize, Serialize};
use tauri::{EventHandler, Manager, Runtime};

/// 应用的所有事件类型以及对应 Payload
#[derive(Debug, Default, Clone)]
pub enum MP4GameEvent {
    /// 未知的事件
    #[default]
    Unknown,
    /// 播放 暂停 或 结束
    PlayFinish(TrackFinishEvent),
    /// 转换 暂停 或 结束
    ConvertFinish(TrackFinishEvent),
    /// 窗口 显示 或 隐藏
    WindowVisibility(bool),
}

/// 播放 暂停 或 结束
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TrackFinishEvent {
    /// 播放器 ID
    pub id: String,
    /// 是否为暂停
    #[serde(rename = "isPause")]
    pub is_pause: bool,
}

/// Unknown 事件名称
pub const UNKNOWN: &str = "";

/// PlayFinish 事件名称
pub const PLAY_FINISH: &str = "midi-play-finish";

/// ConvertFinish 事件名称
pub const CONVERT_FINISH: &str = "midi-convert-finish";

/// WindowVisibility 事件名称
pub const WINDOW_VISIBILITY: &str = "mp4game-window-visibility";

impl MP4GameEvent {
    /// 获取事件名称
    pub fn event_name(&self) -> &'static str {
        match self {
            MP4GameEvent::Unknown => UNKNOWN,
            MP4GameEvent::PlayFinish { .. } => PLAY_FINISH,
            MP4GameEvent::ConvertFinish { .. } => CONVERT_FINISH,
            MP4GameEvent::WindowVisibility { .. } => WINDOW_VISIBILITY,
        }
    }

    /// 向所有 Webview 窗口发送事件
    ///
    /// # 参数
    ///
    /// * `manager` - App 运行管理器
    ///
    /// # 返回值
    ///
    /// * [`Ok`] - 成功
    /// * [`Err`] - 失败
    pub fn emit_all<R: Runtime>(self, manager: &impl Manager<R>) -> tauri::Result<()> {
        let event_name = self.event_name();
        match self {
            MP4GameEvent::Unknown => manager.emit_all(event_name, serde_json::Value::Null),
            MP4GameEvent::PlayFinish(payload) => manager.emit_all(event_name, payload),
            MP4GameEvent::ConvertFinish(payload) => manager.emit_all(event_name, payload),
            MP4GameEvent::WindowVisibility(payload) => manager.emit_all(event_name, payload),
        }
    }

    /// 向所有 Rust Listner 发送事件
    ///
    /// # 参数
    ///
    /// * `manager` - App 运行管理器
    /// 
    /// # 返回值
    ///
    /// * [`Ok`] - 成功
    /// * [`Err`] - 失败
    pub fn trigger_global<R: Runtime>(
        self,
        manager: &impl Manager<R>,
    ) -> Result<(), serde_json::Error> {
        let event_name = self.event_name();
        match self {
            MP4GameEvent::Unknown => Ok(manager.trigger_global(event_name, Some("".to_string()))),
            MP4GameEvent::PlayFinish(payload) => {
                serde_json::to_string(&payload).and_then(|payload| {
                    manager.trigger_global(event_name, Some(payload));
                    Ok(())
                })
            }
            MP4GameEvent::ConvertFinish(payload) => {
                serde_json::to_string(&payload).and_then(|payload| {
                    manager.trigger_global(event_name, Some(payload));
                    Ok(())
                })
            }
            MP4GameEvent::WindowVisibility(payload) => {
                serde_json::to_string(&payload).and_then(|payload| {
                    manager.trigger_global(event_name, Some(payload));
                    Ok(())
                })
            }
        }
    }
}

pub trait ListenMP4GameEvent {
    /// 监听 MP4GameEvent 事件
    fn listen_mp4game_event<F>(&self, event_name: &'static str, handler: F) -> EventHandler
    where
        F: Fn(MP4GameEvent) + Send + 'static;
}

impl<M: Manager<tauri::Wry>> ListenMP4GameEvent for M {
    /// 监听 MP4GameEvent 事件
    fn listen_mp4game_event<F>(&self, event_name: &'static str, handler: F) -> EventHandler
    where
        F: Fn(MP4GameEvent) + Send + 'static,
    {
        self.listen_global(event_name, move |event| {
            event
                .payload()
                .ok_or("MP4GameEvent Payload 获取失败".to_string())
                .and_then(|s| {
                    match event_name {
                        PLAY_FINISH => serde_json::from_str(s).map(MP4GameEvent::PlayFinish),
                        CONVERT_FINISH => serde_json::from_str(s).map(MP4GameEvent::ConvertFinish),
                        WINDOW_VISIBILITY => {
                            serde_json::from_str(s).map(MP4GameEvent::WindowVisibility)
                        }
                        _ => Ok(MP4GameEvent::Unknown),
                    }
                    .map_err(|e| e.to_string())
                })
                .and_then(|event| Ok(handler(event)))
                .or_else(|e| -> Result<(), ()> {
                    log::error!("MP4GameEvent 处理失败: {e}");
                    Ok(())
                })
                .unwrap();
        })
    }
}
